<?php

namespace Wechat\Controller;

use Think\Controller;

class SwooleController extends Controller {

    //Swoole类
    protected $server;

    //Swoole Http Server返回对象
    protected $swooleReturnData;

    public function init() {

        //创建swooleTable
        $table = new \swoole_table(1024);
        $table->column('fd', \swoole_table::TYPE_INT);
        $table->create();

        //创建websocket服务器对象，监听0.0.0.0:9502端口
        $this->server = new \swoole_websocket_server("0.0.0.0", 9502);
        $this->server->set(array(
            //'worker_num' => 4,
            'task_worker_num' => 100,
            'max_request' => 0,
            'dispatch_mode' => 2,
            'daemonize' => 1,
            //'log_file' => __DIR__.'/log/webs_swoole.log',
        ));

        $this->server->table = $table;

        //建立连接
        $this->server->on('Open', function ($serv, $req) {
            print_r("Welcome to Swoole Websocket System! Client " . $req->fd . " Connected\n");
            $this->server->table->set($req->fd, array('fd' => $req->fd));
        });

        //接受客户端发送的activityId并与客户端id绑定
        $this->server->on('Message', function ($_server, $frame) {
            print_r("Message From frame_fd: ".$frame->fd."\n");
        });

        //Http Server操作
        $this->server->on('Request', function ($req, $resp) {

            //跨域处理
            $resp->header("Access-Control-Allow-Origin","*");
            $resp->header("Access-Control-Allow-Headers","X-Requested-With,SIGN");
            $resp->header("","HTTP/1.1 200 OK");
            $resp->header("Status","200 OK");
            $resp->header("Content-Type","application/json; charset=utf-8");

            //获取用户信息
            try{

                //初始化Http Server
                $this->swooleInitialize();

                //获取服务类型
                $requestPath = $this->getPath($req);

                switch ($requestPath) {

                    case '/genOrderRecord':

                        $data['type'] = C('SWOOLE_TASK_TYPE.GENERATE_ORDER');
                        $data['order_id'] = $req->post['orderId'];
                        $this->server->task($data);

                        break;

                    case '/drawPrize':

                        $data['type'] = C('SWOOLE_TASK_TYPE.DRAW_PRIZE');
                        $data['round_id'] = $req->post['roundId'];
                        $this->server->task($data);

                        break;

                    case '/pushNotification':

                        $data['type'] = C('SWOOLE_TASK_TYPE.PUSH_NOTIFICATION');
                        $data['order_id'] = $req->post['orderId'];

                        $this->server->task($data);

                        break;

                    case '/pushWechat':

                        $data['type'] = C('SWOOLE_TASK_TYPE.PUSH_WIN_GROUP_WECHAT');
                        $data['group_id'] = $req->post['groupId'];

                        $this->server->task($data);

                        break;

                }

            } catch (\Exception $e) {
                print_r("Error Code: " .$e->getCode(). ", Error Message: " .$e->getMessage(). "\n");
                $this->swooleErrorReturn('');
            }

            //返回数据
            $resp->end(json_encode($this->swooleReturnData));

        });

        //任务处理
        $this->server->on('Task',function ($serv, $task_id, $from_id, $data) {

            try {

                $type = $data['type'];

                switch ($type) {

                    case C('SWOOLE_TASK_TYPE.GENERATE_ORDER'):

                        $orderId = $data['order_id'];

                        $order = D('Order')->where(array('order_id' => $orderId))->find();

                        $orderRecordMap['round_id'] = $order['round_id'];
                        $lastOrderRecord = D('OrderRecord')->where($orderRecordMap)->order('order_code DESC')->find();

                        if(!isNullOrEmpty($lastOrderRecord)) {
                            $orderCode = $lastOrderRecord['order_code'];
                            for($i=1;$i<=$order['number'];$i++) {
                                $orderCode += 1;
                                $orderRecordData[] = array('create_time' => getCurrentDate(), 'order_code' => $orderCode, 'time_code' => getTimeCode(),
                                    'order_id' => $order['order_id'], 'round_id' => $order['round_id'], 'group_id' => $order['group_id'], 'customer_id' => $order['customer_id']);
                            }
                        } else {
                            $orderCode = 10000000;
                            for($i=1;$i<=$order['number'];$i++) {
                                $orderCode += 1;
                                $orderRecordData[] = array('create_time' => getCurrentDate(), 'order_code' => $orderCode, 'time_code' => getTimeCode(),
                                    'order_id' => $order['order_id'], 'round_id' => $order['round_id'], 'group_id' => $order['group_id'], 'customer_id' => $order['customer_id']);
                            }
                        }

                        D('OrderRecord')->addAll($orderRecordData);

                        $round = D('Round')->alias('r')
                            ->join('LEFT JOIN __PRODUCT__ p ON r.product_id = p.product_id')
                            ->field('r.*, p.price, p.current_round, p.upper_limit_round')
                            ->where(array('r.round_id' => $order['round_id']))
                            ->find();

                        //计算记录数量
                        $orderRecordCount = D('OrderRecord')->where($orderRecordMap)->count();

                        //开奖
                        if(($round['status'] == C('ROUND_STATUS.WAITING')) && ($orderRecordCount >= $round['price'])) {

                            //修改当前轮次状态
                            $roundStatusData['status'] = C('ROUND_STATUS.END');

                            //保存轮次信息
                            D('Round')->where(array('round_id' => $order['round_id']))->save($roundStatusData);

                            //计算轮次
                            $roundCountMap['product_id'] = $round['product_id'];
                            $roundCountMap['status'] = C('ROUND_STATUS.END');
                            $roundCount = D('Round')->where($roundCountMap)->count();

                            //修改商品当前轮次
                            $productData['current_round'] = $roundCount + 1;

                            //保存商品信息
                            D('Product')->where(array('product_id' => $round['product_id']))->save($productData);

                            //添加新的轮次
                            $newRoundData['product_id'] = $round['product_id'];
                            $newRoundData['round_index'] = $productData['current_round'];
                            $newRoundData['create_time'] = getCurrentDate();
                            $newRoundData['status'] = C('ROUND_STATUS.WAITING');
                            $newRoundData['join_number'] = 0;
                            $newRoundData['pie_type'] = C('PIE_TYPE.CASH');
                            $newRoundData['progress'] = 0;
                            $newRoundData['group_number'] = 0;

                            D('Round')->add($newRoundData);

                            //异步开奖
                            curlPost('http://114.215.113.76:9502/drawPrize', array('roundId' => $order['round_id']));

                        }

                        break;

                    case C('SWOOLE_TASK_TYPE.DRAW_PRIZE'):

                        $roundId = $data['round_id'];

                        $round = D('Round')->alias('r')
                            ->join('LEFT JOIN __PRODUCT__ p ON r.product_id = p.product_id')
                            ->field('r.*, p.price, p.product_name')
                            ->where(array('round_id' => $roundId))
                            ->find();

                        $orderRecordMap['round_id'] = $roundId;
                        $lastOrderRecords = D('OrderRecord')
                            ->where($orderRecordMap)
                            ->order('order_code DESC')
                            ->limit(5)
                            ->select();

                        $sql = "SELECT SUM(a.time_code) AS total_time, SUM(LEFT(a.time_code,6))*1000 AS total_main_time
                                FROM (SELECT * FROM __ORDER_RECORD__ WHERE round_id = ".$roundId." ORDER BY order_code DESC LIMIT 5)a";

                        $recordTime = M()->query($sql);

                        if($round['draw_type'] == C('DRAW_TYPE.FIXED')) {

                            $maxTotalTime = 999 * 5 + $recordTime[0]['total_main_time'];
                            $minTotalTime = $recordTime[0]['total_main_time'];

                            $remainder = $round['win_order_code'] - 10000001;

                            $minQuotient = ceil(($minTotalTime - $remainder)/$round['price']);
                            $maxQuotient = floor(($maxTotalTime - $remainder)/$round['price']);

                            $randQuotient = rand($minQuotient,$maxQuotient);

                            $finalTotalTime = $randQuotient * $round['price'] + $remainder;

                            $totalMicroTime = $finalTotalTime - $recordTime[0]['total_main_time'];

                            $microTimeList = rand_num($totalMicroTime,5);

                            foreach ($lastOrderRecords as $key => $orderRecord) {

                                $randMicro = $microTimeList[$key];

                                $randMicro = str_pad($randMicro,3,'0',STR_PAD_LEFT);

                                $orderRecordData['time_code'] = substr($orderRecord['time_code'],0,6).$randMicro;

                                D('OrderRecord')->where(array('record_id' => $orderRecord['record_id']))->save($orderRecordData);

                            }

                            $winOrderCode = $round['win_order_code'];

                        } else {

                            $winOrderCode = $recordTime[0]['total_time'] % $round['price'] + 10000001;

                        }

                        //查找中奖记录
                        $winOrderRecordMap['order_code'] = $winOrderCode;
                        $winOrderRecordMap['round_id'] = $roundId;
                        $winOrderRecord = D('OrderRecord')->where($winOrderRecordMap)->find();

                        //修改轮次状态
                        $roundData['win_order_code'] = $winOrderCode;
                        $roundData['winner_customer_id'] = $winOrderRecord['customer_id'];
                        $roundData['winner_order'] = $winOrderRecord['order_id'];
                        $roundData['draw_time'] = getCurrentDate();
                        $roundData['status'] = C('ROUND_STATUS.END');

                        D('Round')->where(array('round_id' => $roundId))->save($roundData);

                        //查找中奖团
                        $winGroup = D('Group')->where(array('group_id' => $winOrderRecord['group_id']))->find();

                        //修改团胜状态及团奖励
                        $winGroupData['status'] = C('GROUP_STATUS.WIN');
                        $winGroupData['reward'] = $round['price'];

                        D('Group')->where(array('group_id' => $winGroup['group_id']))->save($winGroupData);

                        //修改团灭状态
                        $failureGroupMap['group_id'] = array('neq',$winGroup['group_id']);
                        $failureGroupMap['round_id'] = $roundId;
                        $failureGroupMap['status'] = C('GROUP_STATUS.WAITING_JOIN');

                        $failureGroupData['status'] = C('GROUP_STATUS.FAILURE');

                        D('Group')->where($failureGroupMap)->save($failureGroupData);

                        //修改中奖团用户奖励
                        $sql = "UPDATE __GROUP_MEMBER__ gm
                                SET reward = ROUND(".$round['price']." * gm.number / ".$winGroup['buy_number'].",2)
                                WHERE group_id = ".$winGroup['group_id'];

                        M()->execute($sql);

                        //修改等待分奖订单状态
                        $winGroupOrderMap['group_id'] = $winGroup['group_id'];
                        $winGroupOrderMap['status'] = C('ORDER_STATUS.WAITING_OPEN');

                        $winGroupOrderData['status'] = C('ORDER_STATUS.WAITING_CHOOSE');
                        $winGroupOrderData['update_time'] = getCurrentDate();

                        D('Order')->where($winGroupOrderMap)->save($winGroupOrderData);

                        //修改团灭订单状态
                        $failureGroupOrderMap['group_id'] = array('neq',$winGroup['group_id']);
                        $failureGroupOrderMap['round_id'] = $roundId;
                        $failureGroupOrderMap['status'] = C('ORDER_STATUS.WAITING_OPEN');

                        $failureGroupOrderData['status'] = C('ORDER_STATUS.FAILURE');
                        $failureGroupOrderData['update_time'] = getCurrentDate();

                        D('Order')->where($failureGroupOrderMap)->save($failureGroupOrderData);

                        //团长加入积分
                        $leaderCustomer = D('Customer')->where(array('customer_id' => $winGroup['customer_id']))->find();
                        $leaderCustomerData['point'] = $leaderCustomer['point'] + $winGroup['member_number'];

                        D('Customer')->where(array('customer_id' => $leaderCustomer['customer_id']))->save($leaderCustomerData);

                        //团长加入积分记录
                        $pointRecordData['order_id'] = $winOrderRecord['order_id'];
                        $pointRecordData['create_time'] = getCurrentDate();
                        $pointRecordData['customer_id'] = $leaderCustomer['customer_id'];
                        $pointRecordData['point'] = $winGroup['member_number'];

                        D('PointRecord')->add($pointRecordData);

                        //给中奖用户推送模板消息
                        $winCustomer = D('Customer')->where(array('customer_id' => $winOrderRecord['customer_id']))->find();

                        $winCustomerMessageData['openid'] = $winCustomer['openid'];
                        $winCustomerMessageData['url'] = C('ROOT_URL').'/Wechat/Order/confirmReceive/id/'.$winOrderRecord['order_id'];
                        $winCustomerMessageData['product_name'] = $round['product_name'];
                        $winCustomerMessageData['money'] = $round['price'];

                        winnerMessage($winCustomerMessageData);

                        break;

                    case C('SWOOLE_TASK_TYPE.PUSH_NOTIFICATION'):

                        $orderId = $data['order_id'];

                        $order = D('Order')->alias('o')
                            ->join('LEFT JOIN __CUSTOMER__ c ON o.customer_id = c.customer_id')
                            ->field('o.*, c.nickname, c.headimgurl')
                            ->where(array('order_id' => $orderId))
                            ->find();

                        foreach ($this->server->table as $item) {
                            $this->server->push($item['fd'], json_encode($order));//消息广播给所有客户端
                        }

                        break;

                    case C('SWOOLE_TASK_TYPE.PUSH_WIN_GROUP_WECHAT'):

                        $groupId = $data['group_id'];

                        $group = D('Group')->alias('g')
                            ->join('LEFT JOIN __ROUND__ r ON g.round_id = r.round_id')
                            ->join('LEFT JOIN __PRODUCT__ p ON r.product_id = p.product_id')
                            ->field('g.round_id, g.group_id, p.product_name, p.price, r.winner_customer_id')
                            ->where(array('g.group_id' => $groupId))
                            ->find();

                        //给中奖团其他用户推送模板消息
                        $winGroupCustomerMap['gm.group_id'] = $groupId;
                        $winGroupCustomerMap['gm.customer_id'] = array('neq',$group['winner_customer_id']);

                        $winGroupCustomerList = D('GroupMember')->alias('gm')
                            ->join('LEFT JOIN __CUSTOMER__ c ON gm.customer_id = c.customer_id')
                            ->field('c.customer_id, c.openid')
                            ->where($winGroupCustomerMap)
                            ->select();

                        foreach ($winGroupCustomerList as $winGroupCustomer) {

                            $winGroupMessageData['openid'] = $winGroupCustomer['openid'];
                            $winGroupMessageData['url'] = C('ROOT_URL').'/Wechat/Product/winDetail/id/'.$groupId;
                            $winGroupMessageData['product_name'] = $group['product_name'];
                            $winGroupMessageData['money'] = $group['price'];

                            winnerGroupMessage($winGroupMessageData);

                        }

                        $failureGroupCustomerMap["g.round_id"] = $group["round_id"];
                        $failureGroupCustomerMap["gm.group_id"] = array("neq", $group["group_id"]);
                        $failureGroupCustomerMap['g.status'] = array("not in",array(C('GROUP_STATUS.WAITING_PAY'),C('GROUP_STATUS.SYSTEM_ABANDON')));
                        $failureGroupCustomerList = D("GroupMember")->alias("gm")
                            ->join("left join __GROUP__ g on g.group_id = gm.group_id")
                            ->join("left join __CUSTOMER__ c on c.customer_id = gm.customer_id")
                            ->where($failureGroupCustomerMap)
                            ->field("c.customer_id, c.openid")
                            ->select();

                        foreach ($failureGroupCustomerList as $failureGroupCustomer) {

                            $failureGroupMessageData['openid'] = $failureGroupCustomer['openid'];
                            $failureGroupMessageData['url'] = C('ROOT_URL').'/Wechat/Product/winDetail/id/'.$groupId;
                            $failureGroupMessageData['product_name'] = $group['product_name'];
                            $failureGroupMessageData['money'] = $group['price'];

                            failureGroupMessage($failureGroupMessageData);

                        }

                        break;

                }

            } catch (\Exception $e) {
                print_r("Error Code: " .$e->getCode(). ", Error Message: " .$e->getMessage(). "\n");
            }

            $this->server->finish();

        });

        //完成时记录log
        $this->server->on('Finish', function ($serv, $task_id, $data) {
            try {
                print_r("Task#$task_id finished, data_len=" . strlen($data) . PHP_EOL);
            } catch (\Exception $e) {
                print_r("Error Code: " .$e->getCode(). ", Error Message: " .$e->getMessage(). "\n");
            }
        });

        //关闭事件
        $this->server->on('close', function ($_server, $fd) {

            if($_server->connection_info($fd)['websocket_status']) {

                $_server->table->del($fd);//从table中删除断开的id

                print_r("Connection Close fd: " . $fd . "\n");

            }

        });

        //启动Swoole服务
        $this->server->start();

    }

    //swoole服务器初始化
    public function swooleInitialize() {
        //返回参数初始化
        $this->swooleReturnData = array();
        $this->swooleReturnData['errorCode'] = 200;
        $this->swooleReturnData['errorMsg'] = '';
    }

    //swoole服务器错误代码返回
    public function swooleErrorReturn($errorEnum) {
        //返回数据
        $this->swooleReturnData['errorCode'] = C('ERROR_CODE.' . $errorEnum);
        $this->swooleReturnData['errorMsg'] = C('ERROR_MSG.' . $errorEnum);
    }

    //获取路径
    public function getPath($req) {
        $path = !isNullOrEmpty($req->server['path_info']) ? $req->server['path_info'] : !isNullOrEmpty($req->server['request_uri']) ? $req->server['request_uri'] : '/';
        return $path;
    }

}